useCallback 與 useMemo 都可以在 React 中來做效能優化,差別在於 useCallback 是拿來快取函式,而 useMemo 是拿來快取陣列或物件類型的資料。那今天就來介紹 useMemo~
為什麼要叫 useMemo
呢?因為返回快取的值這技術稱為記憶化(Memoization),這就是這 React hook 的命名原因。那 useMemo
是用來記住運算結果,只有當依賴變化時,才會重新計算結果。
語法:
const cachedValue = useMemo(calculateValue, dependencies)
calculateValue
:是一個函式,會計算要快取的值。要是純函式(pure function)。dependencies
:是一個陣列。
eslint-plugin-react-hooks
的話可以輔助檢查 dependencies
。calculateValue
這個函式,然後計算要快取的值並且回傳。dependencies
:
calculateValue
這個函式來重新計算,並且把計算後的值快取並且回傳。以下例子中,只有當 numbers
發生變化時,useMemo
才會重新計算總和。如果 numbers
沒有變化,那它會直接使用之前計算的結果。
import { useMemo } from 'react';
function SumArray({ numbers }) {
const total = useMemo(() => {
return numbers.reduce((acc, num) => acc + num, 0);
}, [numbers]); // 只有當 numbers 改變時才重新計算
return <div>總和:{total}</div>;
}
calculateValue
這個函式不會有引數 (Argument),像是 () ⇒
,然後會回傳計算結果。假設有一個顯示項目列表 會根據某些條件進行排序。
優化前:
如果每次重新渲染都對列表進行排序,那可能會造成效能問題,特別是當列表項目數量較大時。
function ItemList({ items, sortBy }) {
const sortedItems = items.sort((a, b) => {
if (sortBy === 'asc') return a - b;
if (sortBy === 'desc') return b - a;
});
return (
<ul>
{sortedItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
}
function Parent() {
const items = [5, 3, 8, 1, 2];
const sortBy = 'asc'; // 升序排列
return <ItemList items={items} sortBy={sortBy} />;
}
items
進行排序,無論 items
是否發生變化。優化後:
import { useMemo } from 'react';
function ItemList({ items, sortBy }) {
const sortedItems = useMemo(() => {
return items.slice().sort((a, b) => {
if (sortBy === 'asc') return a - b;
if (sortBy === 'desc') return b - a;
});
}, [items, sortBy]); // 只有 items 或 sortBy 改變時才會重新排序
return (
<ul>
{sortedItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
}
function Parent() {
const items = [5, 3, 8, 1, 2];
const sortBy = 'asc';
return <ItemList items={items} sortBy={sortBy} />;
}
useMemo
可以避免在 items
或 sortBy
沒有改變時,重複執行排序操作。所以是如何達到效能優化?
items
或 sortBy
發生變化時,才會重新計算 sortedItems
。這樣可以避免每次重新渲染都進行排序,從而提升效能,特別是在列表項目較多或計算成本較高的情況下。